package com.hybrid.webview;

import java.util.List;
import java.util.Map;

import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.hybrid.config.GlobalConfig;
import com.hybrid.exeption.WindVaneError;
import com.hybrid.jsbridge.WVCallBackContext;
import com.hybrid.jsbridge.WVJsBridge;
import com.hybrid.jsbridge.WVPluginEntryManager;
import com.hybrid.utils.TaoLog;
import com.hybrid.utils.WVConstants;
import com.hybrid.view.PopupWindowController;

import static android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;

/**
 * 统一封装的WebView 子类，它是整个WindVane的基础核心类。 它内部已经加入了WebViewClient 和 WebChromeClient，
 * 封装了loading View 和 error View， 以及navi bar 支持直接使用 waplugin:// 协议加载url
 * 注意：退出Activity 或者不使用webview 以后必须 调用 {@link #destroy()} 方法
 */
public class WVWebView extends WebView implements Handler.Callback {
    private static final String TAG = "WVWebView";

    private int mWvNativeCallbackId = 1000; // nativeCallback
    // 初始值

    protected Context context;
    protected Handler mHandler = null;

    protected WVWebViewClient webViewClient;
    protected WVWebChromeClient webChromeClient;

    protected boolean isAlive;
    //private WVUIModel wvUIModel = null;
    protected boolean supportDownload = true;
    private boolean wvSupportNativeJs = false;

    // private long LastClickTime = 0;

    protected WVPluginEntryManager entryManager; // jsbridge
    private Callback mCallback;
    private IWebAdvancedCallback mAdvancedCallback;

    public WVWebView(Context ctx, AttributeSet attrs, int defStyle) {
        super(ctx, attrs, defStyle);
        this.context = ctx;
        this.init();
    }

    public WVWebView(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);
        this.context = ctx;
        this.init();
    }

    public WVWebView(Context ctx) {
        super(ctx);
        this.context = ctx;
        this.init();
    }

    @Override
    public void loadData(String data, String mimeType, String encoding) {
        if (!isAlive) {
            return;
        }
        super.loadData(data, mimeType, encoding);
    }

    @Override
    public void reload() {
        super.reload();
    }

    public void refresh() {
        this.reload();
    }

    public void tryClose() {
        if (null != mAdvancedCallback) {
            mAdvancedCallback.onPageClose();
        }
    }

    public void updateMenu(List<MenuItem> menuItemList) {
        if (null != mAdvancedCallback) {
            mAdvancedCallback.onUpdateMenu(menuItemList);
        }
    }

    public void updateTitle(String title) {
        if (null != mAdvancedCallback) {
            mAdvancedCallback.onUpdateTitle(title);
        }
    }

    public void updateAlpha(float alpha) {
        if (null != mAdvancedCallback) {
            mAdvancedCallback.onUpdateStatusBarAlpha(alpha);
        }
    }

    public void tryGoBack(boolean considerClose) {
        if (canGoBack()) {
            // 预防NPE异常导致无法退出webview
            try {
                goBack();
            } catch (Exception e) {
                if (considerClose) {
                    if (null != mAdvancedCallback) {
                        mAdvancedCallback.onPageClose();
                    }
                }
            }
        } else {
            if (null != mAdvancedCallback) {
                mAdvancedCallback.onPageClose();
            }
        }
    }

    @Override
    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String failUrl) {
        if (!isAlive) {
            return;
        }
        if (TaoLog.getLogStatus()) {
            TaoLog.d(TAG, "loadDataWithBaseURL: baseUrl=" + baseUrl);
        }
        super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, failUrl);
    }

    @Override
    public void loadUrl(final String url) {
        if (!isAlive || url == null) {
            return;
        }
        if (TaoLog.getLogStatus()) {
            TaoLog.d(TAG, "loadUrl: url=" + url);
        }
        // 访问wap组件
        //if (url.startsWith(WVH5UrlPluginService.MODULE_PROTOCOL)) {
        //    if (WVH5UrlPluginService.isRegisteredWapModuleHandler()) {
        //        WVH5UrlPluginModelInterface h5PluginModel = WVH5UrlPluginService.registeredWapModuleHandler();
        //        h5PluginModel.gotoModule(context, url, SimpleHybridActivity.class);
        //    }
        //    WVCacheManager.getInstance().updateCacheRule(false);
        //    return;
        //}
        try { //华为D2-2010 底层NPE
            super.loadUrl(url);
        } catch (NullPointerException e) {
            TaoLog.e(TAG, e.getMessage());
        }
        //WVCacheManager.getInstance().updateCacheRule(false);
    }

    public void superLoadUrl(String url) {
        if (!isAlive) {
            return;
        }
        super.loadUrl(url);
    }

    @Override
    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
        if (!isAlive || url == null) {
            return;
        }
        if (TaoLog.getLogStatus()) {
            TaoLog.d(TAG, "loadUrl with headers: url=" + url);
        }
        super.loadUrl(url, additionalHttpHeaders);
        //WVCacheManager.getInstance().updateCacheRule(false);
    }

    @Override
    public void postUrl(String url, byte[] postData) {
        if (!isAlive || url == null) {
            return;
        }
        if (TaoLog.getLogStatus()) {
            TaoLog.d(TAG, "postUrl: url=" + url);
        }
        super.postUrl(url, postData);
    }

    /**
     * 初始化
     */
    @SuppressWarnings("deprecation")
    private void init() {
        mHandler = new Handler(Looper.getMainLooper(), this);
        webViewClient = new WVWebViewClient(context);
        super.setWebViewClient(webViewClient);
        webChromeClient = new WVWebChromeClient(context);
        super.setWebChromeClient(webChromeClient);

        // 默认消除垂直滚动条，解决页面左右滑动问题
        setVerticalScrollBarEnabled(false);
        requestFocus();
        CookieManager.getInstance().setAcceptCookie(true);
        WebSettings setting = this.getSettings();
        // 设置访问文件权限
        setting.setAllowFileAccess(true);
        setting.setJavaScriptEnabled(true);

        String apptag = GlobalConfig.getInstance().getAppName();
        String appversion = GlobalConfig.getInstance().getAppVersion();
        int width = GlobalConfig.getInstance().getWidth();
        int height = GlobalConfig.getInstance().getHeight();
        if (!TextUtils.isEmpty(apptag) && !TextUtils.isEmpty(appversion)) {
            //android(TeachIN/1.0.0)OpenWeb/1.0.0 640*1136
            setting.setUserAgentString(
                setting.getUserAgentString()
                    + " android(" + apptag + "/" + appversion + ")"
                    + GlobalConfig.DEFAULT_UA + " " + width + "*" + height);
        } else {
            setting.setUserAgentString(setting.getUserAgentString() + GlobalConfig.DEFAULT_UA);
        }

        setting.setCacheMode(WebSettings.LOAD_DEFAULT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setting.setMixedContentMode(MIXED_CONTENT_ALWAYS_ALLOW);
        }
        // enable database
        if (Build.VERSION.SDK_INT >= 5) {
            setting.setDatabaseEnabled(true);
            // String dbPath = context.getDir("database",
            // Context.MODE_PRIVATE).getAbsolutePath();
            String dbPath = "/data/data/" + context.getPackageName() + "/databases";
            setting.setDatabasePath(dbPath);
            /*
             * enable Geolocation
             */
            setting.setGeolocationEnabled(true);
            setting.setGeolocationDatabasePath(dbPath);
        }
        // 加快HTML网页加载完成速度
        if (Build.VERSION.SDK_INT >= 19) {
            setting.setLoadsImagesAutomatically(true);
        } else {
            setting.setLoadsImagesAutomatically(false);
        }

        //让WebView支持播放插件
        setting.setPluginState(WebSettings.PluginState.ON);

        if (Build.VERSION.SDK_INT >= 7) {
            /*
             * enable local storage
             */
            setting.setDomStorageEnabled(true);
            /*
             * enable appcache
             */
            setting.setAppCacheEnabled(true);

            if (context != null && context.getCacheDir() != null) {
                String cacheDir = context.getCacheDir().getAbsolutePath();
                setting.setAppCachePath(cacheDir);
            }

        }
        if (Build.VERSION.SDK_INT < 18) {
            setting.setSavePassword(false);
        }
        // 设置页面字体大小，覆盖系统设置
        if (Build.VERSION.SDK_INT >= 14) {
            setting.setTextZoom(100);
        } else {
            setting.setTextSize(WebSettings.TextSize.NORMAL);
        }

        if (TaoLog.getLogStatus() && Build.VERSION.SDK_INT >= 19) {
            WebView.setWebContentsDebuggingEnabled(true);
        }

        WVJsBridge.getInstance().init();
        // 初始化 Js API
        entryManager = new WVPluginEntryManager(context, this);
        //WVAppEvent event = new WVAppEvent();
        //event.initialize(context, this);
        //this.addJsObject("AppEvent", event);

        /**
         * 解决老版本下系统自动添加 searchBoxJavaBridge_ 导致的安全问题
         */
        if (Build.VERSION.SDK_INT > 10 && Build.VERSION.SDK_INT < 17) {
            try {
                this.removeJavascriptInterface("searchBoxJavaBridge_");
            } catch (Throwable e) { // 在部分机型上不存在此方法，保护一下
                e.printStackTrace();
            }
        }

        // 规避三星原生的webview crash问题
        if (Build.VERSION.SDK_INT > 15) {
            try {
                ClipboardManager clipboard = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
                if (clipboard != null) {
                    ClipData clip = clipboard.getPrimaryClip();
                    if (clip == null) {
                        clipboard.setPrimaryClip(ClipData.newPlainText("初始化", ""));
                    } else if (("intent:#Intent;S.K_1171477665=;end").equals(clip.getItemAt(0).coerceToText(context)
                        .toString())) {
                        clipboard.setPrimaryClip(ClipData.newPlainText("初始化", ""));
                    }
                }
            } catch (Exception e) {

            }
        }
        //wvUIModel = new WVUIModel(context, this);

        this.setOnLongClickListener(new OnLongClickListener() {

            @Override
            public boolean onLongClick(View v) {
                HitTestResult result = null;
                try { // will crash on mi pad
                    result = WVWebView.this.getHitTestResult();
                } catch (Exception e) {
                }
                if (result == null) {
                    return false;
                }
                if (TaoLog.getLogStatus()) {
                    TaoLog.d(TAG, "Long click on WebView, " + result.getExtra());

                }
                if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE
                    || result.getType() == HitTestResult.IMAGE_TYPE) {
                    mImageUrl = result.getExtra();
                    mPopupController = new PopupWindowController(context, WVWebView.this, mPopupMenuTags,
                        popupClickListener);
                    mPopupController.show();
                    return true;
                }
                return false;
            }
        });
        setDownloadListener(new WVDownLoadListener());

        WVTweakWebCoreHandler.tryTweakWebCoreHandler();
        isAlive = true;

    }

    @Override
    public void setWebViewClient(WebViewClient client) {

        if (client instanceof WVWebViewClient) {
            this.webViewClient = (WVWebViewClient)client;
            super.setWebViewClient(client);
        } else {
            throw new WindVaneError("Your WebViewClient must be extended from WVWebViewClient");
        }
    }

    @Override
    public void setWebChromeClient(WebChromeClient client) {

        if (client instanceof WVWebChromeClient) {
            this.webChromeClient = (WVWebChromeClient)client;
            super.setWebChromeClient(client);
        } else {
            throw new WindVaneError("Your WebChromeClient must be extended from WVWebChromeClient");
        }
    }

    // // 设置拦截的过滤器
    // public void setUrlFilter(UrlFilter filter) {
    // webViewClient.setUrlFilter(filter);
    // }
    //
    // public UrlFilter getUrlFilter() {
    // return webViewClient.getUrlFilter();
    // }

    @Override
    public void destroy() {
        /*
         防止反复调用destroy的npe
          */
        if (!isAlive) {
            return;
        }
        isAlive = false;
        // 解绑
        super.setWebViewClient(null);
        super.setWebChromeClient(null);
        this.webViewClient = null;
        this.webChromeClient = null;
        this.entryManager.onDestroy();
        //WVMemoryCache.getInstance().evictAll();

        this.mHandler.removeCallbacksAndMessages(null);
        this.removeAllViews();

        /**
         *有些极少数机型调destroy会挂，内部做下保护
         */
        try {
            super.destroy();
        } catch (Exception e) {
        }
    }

    public boolean isAlive() {
        return isAlive;
    }

    //public WVUIModel getWvUIModel() {
    //    return wvUIModel;
    //}

    private long onErrorTime = 0L;

    public void onMessage(int id, Object data) {
        if (mHandler != null) {
            Message msg = Message.obtain();
            msg.what = id;
            msg.obj = data;
            mHandler.sendMessage(msg);
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case WVConstants.NOTIFY_PAGE_START: {
                if (mCallback != null) {
                    mCallback.onPageStarted((String)msg.obj);
                }
                //wvUIModel.showLoadingView();
                //wvUIModel.switchNaviBar(1);
                return true;
            }
            case WVConstants.NOTIFY_PAGE_FINISH: {
                if (mCallback != null) {
                    mCallback.onReceivedTitle(getTitle());
                    mCallback.onPageFinished((String)msg.obj);
                }

                //wvUIModel.hideLoadingView();
                //wvUIModel.resetNaviBar();
                //if (onErrorTime != 0 && (System.currentTimeMillis() - onErrorTime) > 3000) {
                //    wvUIModel.hideErrorPage();
                //}
                return true;
            }
            case WVConstants.NOTIFY_PAGE_PROGRESS:
                if (mCallback != null) {
                    mCallback.onProgressChanged((Integer)msg.obj);
                }
                return true;
            case WVConstants.NOTIFY_PAGE_ERROR: {
                if (mCallback != null) {
                    mCallback.onReceivedError(this, (String)msg.obj);
                }
                //wvUIModel.loadErrorPage();
                //onErrorTime = System.currentTimeMillis();
                // wvUIModel.hideLoadingView();
                return true;
            }
            case WVConstants.NOTIFY_TIME_OUT: {
                //wvUIModel.hideLoadingView();
                return true;
            }
            case WVConstants.NOTIFY_SAVE_IMAGE_SUCCESS: {
                Toast.makeText(context, "图片保存到相册成功", Toast.LENGTH_LONG).show();
                return true;
            }
            case WVConstants.NOTIFY_SAVE_IMAGE_FAIL: {
                Toast.makeText(context, "图片保存到相册失败", Toast.LENGTH_LONG).show();
                return true;
            }
            default:
                break;
        }
        return false;
    }

    private PopupWindowController mPopupController;
    private String[] mPopupMenuTags = {"保存到相册"};
    private String mImageUrl;

    private OnClickListener popupClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mPopupMenuTags != null && mPopupMenuTags.length > 0) {
                if (mPopupMenuTags[0].equals(v.getTag())) {
                    //ImageTool.saveImageToDCIM(context.getApplicationContext(), mImageUrl, mHandler);
                }
            }
            mPopupController.hide();
        }
    };

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        if (entryManager != null) {
            entryManager.onScrollChanged(l, t, oldl, oldt);
        }
        try {
            super.onScrollChanged(l, t, oldl, oldt);
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }

    @Override
    @TargetApi(11)
    public void onPause() {
        //WVMemoryCache.getInstance().evictAll();
        if (entryManager != null) {
            entryManager.onPause();
        }
        if (Build.VERSION.SDK_INT >= 11) {
            super.onPause();
        }
        //WVEventService.getInstance().onEvent(WVEventId.PAGE_onPause);
    }

    @Override
    public void pauseTimers() {
        if (TaoLog.getLogStatus()) {
            TaoLog.e(TAG, "You  must be careful  to Call pauseTimers ,It's Global");
        }
    }

    @Override
    public void resumeTimers() {
        super.resumeTimers();
        if (TaoLog.getLogStatus()) {
            TaoLog.e(TAG, "You  must be careful  to Call resumeTimers ,It's Global");
        }
    }

    @Override
    @TargetApi(11)
    public void onResume() {
        if (entryManager != null) {
            entryManager.onResume();
        }
        //if (Build.VERSION.SDK_INT >= 11) {
        //    super.onResume();
        //}
        //WVEventService.getInstance().onEvent(WVEventId.PAGE_onResume);
    }

    public Handler getWVHandler() {
        return mHandler;
    }

    class WVDownLoadListener implements DownloadListener {
        @Override
        public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype,
                                    long contentLength) {
            if (TaoLog.getLogStatus()) {
                TaoLog.d(TAG,
                    "Download start, url: " + url + " contentDisposition: " + contentDisposition + " mimetype: "
                        + mimetype
                        + " contentLength: " + contentLength);
            }
            if (!supportDownload) {
                TaoLog.w(TAG, "DownloadListener is not support for webview.");
                return;
            }
            Uri uri = Uri.parse(url);
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            try {
                context.startActivity(intent);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(context, "对不起，您的设备找不到相应的程序", Toast.LENGTH_LONG).show();
                TaoLog.e(TAG, "DownloadListener not found activity to open this url.");
            }
        }
    }

    public void setSupportDownload(boolean support) {
        this.supportDownload = support;
    }

    @Override
    public void addJavascriptInterface(Object object, String name) {
        if (!wvSupportNativeJs && Build.VERSION.SDK_INT < 17) {
            // throw new
            // WindVaneError("It is dangerous to use this method for API level 16 or below.");
            TaoLog.e(TAG, "addJavascriptInterface is disabled before API level 17 for security reason.");
            super.addJavascriptInterface(object, name);
        } else {
            super.addJavascriptInterface(object, name);
        }
    }

    public void supportJavascriptInterface(boolean support) {
        wvSupportNativeJs = support;
    }

    /**
     * 添加jsbridge对象
     *
     * @param name     对应的前端访问对象名
     * @param instance name所对应的jsbridge处理对象实例
     */
    public void addJsObject(String name, Object instance) {
        if (entryManager != null) {
            entryManager.addEntry(name, instance);
        }
    }

    public Object getJsObject(String name) {
        if (entryManager == null) {
            return null;
        }
        return entryManager.getEntry(name);
    }

    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (entryManager != null) {
            entryManager.onActivityResult(requestCode, resultCode, intent);
        }
    }

    public WVCallBackContext getWVCallBackContext() {
        return new WVCallBackContext(this);
    }

    /**
     * 拼接脚本，使loadUrl后可获取其返回值
     *
     * @param doScript 需要执行的js
     */
    public void script2NativeCallback(String doScript, ValueCallback<String> callback) {
        int id = ++mWvNativeCallbackId;
        //WVNativeCallbackUtil.putNativeCallbak(String.valueOf(id), callback);
        String script = "javascript:console.log('wvNativeCallback/" + id + "/'+function(){var s = " + doScript
            + "; return (typeof s === 'object' ? JSON.stringify(s) : typeof s === 'string' ? '\"' + s + '\"' : s);}())";
        this.loadUrl(script);
    }

    public void evaluateJavascript(String script) {
        this.evaluateJavascript(script, null);
    }

    public void fireEvent(String event) {
        this.fireEvent(event, "{}");
    }

    public void fireEvent(String event, String data) {
        this.getWVCallBackContext().fireEvent(event, data);
    }

    private static boolean evaluateJavascriptSupported = Build.VERSION.SDK_INT >= 19;

    @Override
    public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
        //        if ("javascript:".equals(script.substring(0, 11).toLowerCase())) {
        if (script.toLowerCase().startsWith("javascript:")) {
            script = script.substring(11);
        }
        // Android 4.4上无论是否有回调均可直接用原生的
        // 但部分机型依然不支持,小米hm note 1ltetd是2.x的系统，伪装成了4.4.2
        if (evaluateJavascriptSupported) {
            try {
                super.evaluateJavascript(script, resultCallback);
            } catch (NoSuchMethodError e) {
                evaluateJavascriptSupported = false;
                evaluateJavascript(script, resultCallback);
            } catch (IllegalStateException e) {
                evaluateJavascriptSupported = false;
                evaluateJavascript(script, resultCallback);
            }
        } else if (resultCallback == null) { // 无回调的走loadurl
            this.loadUrl("javascript:" + script);
        } else { // 有回调的走console.log形式
            this.script2NativeCallback(script, resultCallback);
        }
    }

    /**
     * 设置接口
     *
     * @param callback 接口
     */
    public void actionSetCallback(Callback callback) {
        mCallback = callback;
    }

    public void setAdvancedCallback(IWebAdvancedCallback webAdvancedCallback) {
        mAdvancedCallback = webAdvancedCallback;
    }

    public interface Callback {
        void onReceivedTitle(String title);

        void onPageStarted(String url);

        void onPageFinished(String url);

        void onProgressChanged(int progress);

        void onReceivedError(WebView view, String failingUrl);
    }

    public interface IWebAdvancedCallback {
        /**
         * 页面关闭
         */
        void onPageClose();

        /**
         * 更多Menu更新
         *
         * @param menuItemList Menu列表
         */
        void onUpdateMenu(List<MenuItem> menuItemList);

        /**
         * Title更新
         *
         * @param title 标题
         */
        void onUpdateTitle(String title);

        void onUpdateStatusBarAlpha(float alpha);
    }
}
